# tests/test_family.py
from datetime import timedelta

from django.urls import reverse
from django.utils import timezone
from rest_framework.test import APITestCase
from rest_framework import status
from django.contrib.auth.models import User
from rest_framework_simplejwt.tokens import RefreshToken

from apps.user.models import Family, UserProfile, FamilyMember, InvitationCode, SessionLog


# tests.py
class FamilyAPITest(APITestCase):
    def setUp(self):
        self.user = User.objects.create_user(username='testuser', password='12345')
        self.profile = UserProfile.objects.create(user=self.user)
        self.client.force_authenticate(user=self.user)

    def test_create_family_success(self):
        """测试正常创建家庭"""
        url = reverse('family-list')
        data = {'name': '幸福之家'}

        # 发送创建请求
        response = self.client.post(url, data)

        # 验证响应
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertIn('id', response.data)
        self.assertEqual(response.data['name'], '幸福之家')

        # 验证数据库记录
        family = Family.objects.get(id=response.data['id'])
        self.assertEqual(family.manager.user.username, 'testuser')

        # 验证家庭成员关系
        member = FamilyMember.objects.get(family=family)
        self.assertEqual(member.user.username, 'testuser')
        self.assertEqual(member.role, 'admin')

    def test_create_duplicate_family(self):
        """测试重复创建家庭"""
        # 先创建成功
        self.client.post(reverse('family-list'), {'name': '第一个家庭'})

        # 尝试再次创建
        response = self.client.post(reverse('family-list'), {'name': '第二个家庭'})

        # 应返回403禁止访问
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertEqual(response.data['detail'], '用户已加入其他家庭')

    def test_create_with_missing_name(self):
        """测试缺失必要字段"""
        response = self.client.post(reverse('family-list'), {})
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('name', response.data)

    def test_unauthenticated_create(self):
        """测试未认证访问"""
        self.client.logout()  # 清除认证

        response = self.client.post(reverse('family-list'), {'name': '匿名家庭'})
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_family_members_list(self):
        """测试家庭成员列表接口"""
        # 创建测试家庭
        family = Family.objects.create(name="测试家庭", manager=self.profile)
        FamilyMember.objects.create(user=self.user, family=family, role='admin')

        # 调用接口
        url = reverse('family-members')  # 确保路由名为family-members
        response = self.client.get(url)

        # 验证响应
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]['username'], 'testuser')
        self.assertEqual(response.data[0]['role'], '管理员')  # 根据get_role_display配置

    def test_members_with_multiple_users(self):
        """测试包含多个成员的情况"""
        # 创建家庭并添加成员
        family = Family.objects.create(name="大家庭", manager=self.profile)
        FamilyMember.objects.create(user=self.user, family=family, role='admin')

        # 添加第二个成员
        user2 = User.objects.create_user(username='member2', password='123')
        FamilyMember.objects.create(user=user2, family=family, role='member')

        # 获取列表
        response = self.client.get(reverse('family-members'))

        # 验证结果
        self.assertEqual(len(response.data), 2)
        roles = [item['role'] for item in response.data]
        self.assertIn('管理员', roles)
        self.assertIn('成员', roles)


class FamilyInviteTests(APITestCase):
    def setUp(self):
        # 创建管理员用户
        self.admin = User.objects.create_user(username='admin', password='pass123')
        self.admin_profile = UserProfile.objects.create(user=self.admin)

        # 创建普通用户
        self.user = User.objects.create_user(username='member', password='pass123')
        self.user_profile = UserProfile.objects.create(user=self.user)

        # 创建测试家庭
        self.family = Family.objects.create(name="测试家庭", manager=self.admin_profile)
        FamilyMember.objects.create(user=self.admin, family=self.family, role='admin')

        # 认证为管理员
        self.client.force_authenticate(user=self.admin)

    # 生成邀请码测试
    def test_generate_invite_success(self):
        """测试管理员生成有效邀请码"""
        url = reverse('family-generate-invite')
        response = self.client.post(url)

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertTrue(InvitationCode.objects.exists())
        expires_str = response.data['expires_at'].strftime('%Y-%m-%dT%H:%M:%S')
        expected_str = (timezone.now() + timedelta(hours=24)).strftime('%Y-%m-%dT%H:%M:%S')
        self.assertEqual(expires_str[:19], expected_str[:19])

    # 加入家庭测试
    def test_join_family_success(self):
        """测试使用有效邀请码加入"""
        # 生成有效邀请码
        code = InvitationCode.objects.create(
            family=self.family,
            created_by=self.admin,
            expires_at=timezone.now() + timedelta(hours=24)
        )

        # 新用户加入
        new_user = User.objects.create_user(username='newbie', password='pass123')
        self.client.force_authenticate(user=new_user)

        response = self.client.post(reverse('family-join'), {'code': str(code.code)})
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue(FamilyMember.objects.filter(user=new_user).exists())
        self.assertTrue(InvitationCode.objects.get(pk=code.pk).used)

    # 离开家庭测试
    def test_leave_family_as_member(self):
        """测试普通成员退出家庭"""
        # 添加测试成员
        member = User.objects.create_user(username='test_member', password='pass123')
        FamilyMember.objects.create(user=member, family=self.family, role='member')
        self.client.force_authenticate(user=member)

        response = self.client.delete(reverse('family-leave'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(FamilyMember.objects.filter(user=member).exists())

    def test_leave_family_as_last_admin(self):
        """测试最后的管理员退出解散家庭"""
        response = self.client.delete(reverse('family-leave'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(Family.objects.filter(pk=self.family.pk).exists())

    def test_leave_with_admin_transfer(self):
        """测试管理员退出时权限转移"""
        # 添加另一个成员
        new_admin = User.objects.create_user(username='new_admin', password='pass123')
        FamilyMember.objects.create(user=new_admin, family=self.family, role='member')
        UserProfile.objects.create(user=new_admin)

        response = self.client.delete(reverse('family-leave'))
        updated_family = Family.objects.get(pk=self.family.pk)
        self.assertEqual(updated_family.manager.user, new_admin)




class AuthViewSetTests(APITestCase):
    def setUp(self):
        # 预创建测试用户
        self.existing_user = User.objects.create_user(
            username='existing_user',
            password='Test@123'
        )
        UserProfile.objects.create(user=self.existing_user, nickname="老用户")

    # 注册测试组
    def test_register_success(self):
        """测试正常注册流程"""
        url = reverse('auth-register')
        data = {
            "username": "new_user",
            "password": "SecurePass123!",
            "nickname": "测试用户"
        }

        response = self.client.post(url, data, format='json')

        # 验证响应
        self.assertEqual(response.status_code, 201)
        self.assertTrue(response.json()['ok'])
        self.assertIn('tokens', response.json()['data'])

        # 验证数据库记录
        self.assertTrue(User.objects.filter(username='new_user').exists())
        profile = UserProfile.objects.get(user__username='new_user')
        self.assertEqual(profile.nickname, "测试用户")

    def test_register_missing_fields(self):
        """测试缺失必填字段"""
        url = reverse('auth-register')
        data = {"username": "incomplete_user"}

        response = self.client.post(url, data, format='json')

        self.assertEqual(response.status_code, 422)
        self.assertEqual(response.json()['error']['code'], "MISSING_FIELDS")

    def test_register_duplicate_username(self):
        """测试重复用户名注册"""
        url = reverse('auth-register')
        data = {
            "username": "existing_user",
            "password": "AnotherPass123",
            "nickname": "重复用户"
        }

        response = self.client.post(url, data, format='json')

        self.assertEqual(response.status_code, 409)
        self.assertEqual(response.json()['error']['code'], "USER_EXISTS")

    # 登录测试组
    def test_login_success(self):
        """测试有效凭证登录"""
        url = reverse('auth-login')
        data = {
            "username": "existing_user",
            "password": "Test@123"
        }

        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, 200)
        self.assertIn('access', response.json()['data'])

        # 验证会话记录
        session = SessionLog.objects.last()
        self.assertEqual(session.user.username, "existing_user")

    def test_login_invalid_credentials(self):
        """测试无效密码登录"""
        url = reverse('auth-login')
        data = {
            "username": "existing_user",
            "password": "WrongPassword"
        }

        response = self.client.post(url, data, format='json')

        self.assertEqual(response.status_code, 401)
        self.assertEqual(response.json()['error']['code'], "INVALID_CREDENTIALS")

    # 注销测试组
    def test_logout_success(self):
        """测试有效注销流程"""
        # 获取有效Token
        refresh = RefreshToken.for_user(self.existing_user)
        access_token = str(refresh.access_token)

        # 设置认证头
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {access_token}')

        # 发送请求
        response = self.client.post(
            reverse('auth-logout'),
            {"refresh": str(refresh)},
            format='json'
        )

        self.assertEqual(response.status_code, 200)
        self.assertTrue(response.json()['ok'])

    def test_logout_missing_token(self):
        """测试缺失Refresh Token"""
        # 先进行认证
        refresh = RefreshToken.for_user(self.existing_user)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')

        response = self.client.post(reverse('auth-logout'), {}, format='json')
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()['error']['code'], "MISSING_TOKEN")

    # Token刷新测试组
    def test_refresh_success(self):
        """测试有效Token刷新"""
        refresh = RefreshToken.for_user(self.existing_user)

        url = reverse('auth-refresh')
        data = {"refresh": str(refresh)}

        response = self.client.post(url, data, format='json')

        self.assertEqual(response.status_code, 200)
        self.assertIn('access', response.json()['data'])

    def test_refresh_invalid_token(self):
        """测试无效Token刷新"""
        url = reverse('auth-refresh')
        data = {"refresh": "invalid.token.here"}

        response = self.client.post(url, data, format='json')

        self.assertEqual(response.status_code, 401)
        self.assertEqual(response.json()['error']['code'], "INVALID_TOKEN")

    # 用户资料测试组
    def test_get_profile_authenticated(self):
        """测试认证用户获取资料"""
        # 获取Token并设置认证
        refresh = RefreshToken.for_user(self.existing_user)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')

        url = reverse('auth-profile')
        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json()['nickname'], "老用户")

    def test_get_profile_unauthenticated(self):
        """测试未认证访问资料"""
        url = reverse('auth-profile')
        response = self.client.get(url)

        self.assertEqual(response.status_code, 401)